namespace Org.Loon.Framework.Xna.Game.Action
{
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;

    using Org.Loon.Framework.Xna.Game.Utils;
    using Org.Loon.Framework.Xna.Game.Utils.Collection;
    using Org.Loon.Framework.Xna.Game.Core.Graphics.Component;
    
    public class Actions
    {

        private readonly ArrayMap actions;

        internal Actions()
        {
            this.actions = new ArrayMap(CollectionUtils.INITIAL_CAPACITY);
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Clear()
        {
            actions.Clear();
        }

        public void AddAction(ActionEvent action, Actor actObject,
                bool paused)
        {
            Actions.ActionElement element = (Actions.ActionElement)actions.Get(actObject);
            if (element == null)
            {
                element = new Actions.ActionElement(actObject, paused);
                actions.Put(actObject, element);
            }
            CollectionUtils.Add(element.actions, action);
            action.Start(actObject);
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        private void DeleteElement(Actions.ActionElement element)
        {
            CollectionUtils.Clear(element.actions);
            actions.Remove(element.key);
        }

        public void RemoveAllActions(Actor actObject)
        {
            if (actObject == null)
            {
                return;
            }
            Actions.ActionElement element = (Actions.ActionElement)actions.Get(actObject);
            if (element != null)
            {
                CollectionUtils.Clear(element.actions);
                DeleteElement(element);
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        private void RemoveAction(int index, Actions.ActionElement element)
        {
            CollectionUtils.RemoveAt(element.actions, index);
            if (element.actionIndex >= index)
            {
                element.actionIndex--;
            }
            if ((element.actions.Count == 0))
            {
                DeleteElement(element);
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public int GetCount()
        {
            return actions.Size();
        }

        public void RemoveAction(object tag, Actor actObject)
        {
            Actions.ActionElement element = (Actions.ActionElement)actions.Get(actObject);
            if (element != null)
            {
                if (element.actions != null)
                {
                    int limit = element.actions.Count;
                    for (int i = 0; i < limit; i++)
                    {
                        ActionEvent a = (ActionEvent)element.actions[i];
                        if (a.GetTag() == tag && a.GetOriginal() == actObject)
                        {
                            RemoveAction(i, element);
                        }
                    }
                }
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void RemoveAction(ActionEvent action)
        {
            if (action == null)
            {
                return;
            }
            Actions.ActionElement element = (Actions.ActionElement)actions.Get(action
                    .GetOriginal());
            if (element != null)
            {
                int i = element.actions.IndexOf(action);
                if (i != -1)
                {
                    RemoveAction(i, element);
                }
            }
        }

        public ActionEvent GetAction(object tag, Actor actObject)
        {
            Actions.ActionElement element = (Actions.ActionElement)actions.Get(actObject);
            if (element != null)
            {
                if (element.actions != null)
                {
                    int limit = element.actions.Count;
                    for (int i = 0; i < limit; i++)
                    {
                        ActionEvent a = (ActionEvent)element.actions[i];
                        if (a.GetTag() == tag)
                            return a;
                    }
                }
            }
            return null;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Update(long elapsedTime)
        {
            int size = actions.Size();
            for (int i = size - 1; i > -1; --i)
            {
                Actions.ActionElement currentTarget = (Actions.ActionElement)actions.Get(i);
                if (currentTarget == null)
                {
                    continue;
                }
                lock (currentTarget)
                {
                    if (!currentTarget.paused)
                    {
                        for (currentTarget.actionIndex = 0; currentTarget.actionIndex < currentTarget.actions.Count; currentTarget.actionIndex++)
                        {
                            currentTarget.currentAction = (ActionEvent)currentTarget.actions[currentTarget.actionIndex];
                            if (currentTarget.currentAction == null)
                            {
                                continue;
                            }
                            if (!currentTarget.currentAction.isInit)
                            {
                                currentTarget.currentAction.isInit = true;
                                currentTarget.currentAction.OnLoad();
                            }
                            currentTarget.currentAction.Step(elapsedTime);
                            if (currentTarget.currentAction.IsComplete())
                            {
                                currentTarget.currentAction.Stop();
                                RemoveAction(currentTarget.currentAction);
                            }
                            currentTarget.currentAction = null;
                        }
                    }
                    if ((currentTarget.actions.Count == 0))
                    {
                        DeleteElement(currentTarget);
                    }
                }
            }
        }

        public void Paused(bool pause, Actor actObject)
        {
            Actions.ActionElement element = (Actions.ActionElement)actions.Get(actObject);
            if (element != null)
            {
                element.paused = pause;
            }
        }

        public void Stop(Actor actObject)
        {
            Actions.ActionElement element = (Actions.ActionElement)actions.Get(actObject);
            if (element != null)
            {
                element.paused = true;
            }
        }

        public void Start(Actor actObject)
        {
            Actions.ActionElement element = (Actions.ActionElement)actions.Get(actObject);
            if (element != null)
            {
                element.paused = false;
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Start()
        {
            for (int i = 0; i < actions.Size(); i++)
            {
                ((Actions.ActionElement)actions.Get(i)).paused = false;
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Stop()
        {
            for (int i = 0; i < actions.Size(); i++)
            {
                ((Actions.ActionElement)actions.Get(i)).paused = true;
            }
        }

        internal sealed class ActionElement
        {

            internal Actor key;

            internal int actionIndex;

            internal bool paused;

            internal List<ActionEvent> actions;

            internal ActionEvent currentAction;

            public ActionElement(Actor k, bool v)
            {
                this.actions = new List<ActionEvent>(
                        CollectionUtils.INITIAL_CAPACITY);
                this.key = k;
                this.paused = v;
            }
        }
    }
}
